package com.company.beauty.common.handler;

import com.company.nuwa.common.constants.Headers;
import com.company.nuwa.common.utils.JsonUtils;
import com.company.nuwa.common.utils.RequestContextUtil;
import com.google.common.collect.Maps;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>全局日志切面</p >
 *
 * @author youyoukele
 * @version 1.0
 * @date 2022/9/18
 */
@Order(Ordered.HIGHEST_PRECEDENCE + 3)
@Aspect
@Slf4j
public class LogHandler {

    private static final String TRACE_ID = "traceId";

    @Pointcut("execution(* com.company..controller..*(..))")
    private void controller() {
    }

    @Pointcut("execution(* com.company..facade..*(..))")
    private void facade() {
    }

    @Before(value = "facade() || controller()")
    public void before(JoinPoint joinPoint) {
        try {
            // 获取当前线程ID
            Thread currentThread = Thread.currentThread();
            long threadId = currentThread.getId();
            Object[] args = joinPoint.getArgs();
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            Parameter[] parameters = method.getParameters();
            List<Parameter> parameterList = Arrays.stream(parameters).collect(Collectors.toList());
            List<Object> argList = Arrays.stream(args).collect(Collectors.toList());
            Map<String, Object> parameterMap = Maps.newHashMap();
            for (int i = 0; i < parameterList.size(); i++) {
                Parameter parameter = parameterList.get(i);
                Object arg = argList.get(i);
                if (arg instanceof HttpServletRequest) {
                    continue;
                }
                if (arg instanceof HttpServletResponse) {
                    continue;
                }
                if (arg instanceof InputStream) {
                    continue;
                }
                if (arg instanceof MultipartFile) {
                    continue;
                }
                parameterMap.put(parameter.getName(), arg);
            }
            MDC.put(TRACE_ID, RequestContextUtil.getHeader(Headers.TRACE_ID));
            log.info("threadId:{},uri:{},{}#{}+request:{}", threadId, RequestContextUtil.getRequestURI(),
                    method.getDeclaringClass().getSimpleName(), method.getName(), JsonUtils.objectToJson(parameterMap));
        } catch (Exception e) {
            log.error("LogAspect#before", e);
        }
    }

    @AfterReturning(value = "facade() || controller()", returning = "rvt")
    public void after(JoinPoint joinPoint, Object rvt) {
        try {
            // 获取当前线程ID
            Thread currentThread = Thread.currentThread();
            long threadId = currentThread.getId();
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            log.info("threadId:{},uri:{},{}#{}+response:{}", threadId, RequestContextUtil.getRequestURI(),
                    method.getDeclaringClass().getSimpleName(), method.getName(),
                    JsonUtils.objectToJson(rvt));
        } catch (Exception e) {
            log.error("LogAspect#after", e);
        } finally {
            MDC.clear();
        }
    }

}
